package org.zh.rc.dao;

import com.alibaba.druid.util.JdbcUtils;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.core.type.TypeReference;
import org.springframework.stereotype.Repository;
import org.zh.entity.MethodBean;
import org.zh.entity.ServiceEntity;

import javax.sql.DataSource;
import java.io.IOException;
import java.sql.*;
import java.time.Instant;
import java.util.*;
import java.util.stream.Collectors;

import static org.zh.util.CommonStatic.om;
import static org.zh.util.CommonStatic.smartHttpPOSTJSON;

@Repository
public class RCJDBC {

    private static final String insert = "/remoteCache/insert";

    private final DataSource dataSource;

    public RCJDBC(DataSource dataSource) {
        this.dataSource = dataSource;
    }

    public Connection getConn() {
        try {
            return dataSource.getConnection();
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }

    public void initTables() {
        Connection conn = getConn();
        try {
            Statement stmt = conn.createStatement();
            String createServiceEntityTableSQL = """
                    create table if not exists family.service_entity
                       (
                           id                 int auto_increment
                               primary key,
                           applicationName    varchar(50)   null,
                           applicationBaseUrl varchar(200)  null,
                           methodsJson        varchar(3000) null,
                           createTime         timestamp     null,
                           survivalTime       timestamp     null,
                           pubClass           varchar(1000) null,
                           subClass           varchar(1000) null
                       );""";
            stmt.executeUpdate(createServiceEntityTableSQL);
            JdbcUtils.close(stmt);
            JdbcUtils.close(conn);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    public void insertBean(ServiceEntity se) {
        String insertSQL = "INSERT INTO service_entity (applicationName,applicationBaseUrl,methodsJson,pubClass,subClass,createTime,survivalTime) values(?,?,?,?,?,?,?)";
        try {
            Connection conn = getConn();
            PreparedStatement stmt = conn.prepareStatement(insertSQL);
            stmt.setString(1, se.getApplicationName());
            stmt.setString(2, se.getApplicationBaseUrl());
            stmt.setString(3, om.writeValueAsString(se.getMethodsJson()));
            stmt.setString(4, om.writeValueAsString(se.getPubClass()));
            stmt.setString(5, om.writeValueAsString(se.getSubClass()));
            stmt.setTimestamp(6, Timestamp.from(Instant.now()));
            stmt.setTimestamp(7, Timestamp.from(Instant.now()));
            stmt.executeUpdate();
            JdbcUtils.close(stmt);
            JdbcUtils.close(conn);
        } catch (SQLException | JsonProcessingException e) {
            throw new RuntimeException(e);
        }
    }

    public void getSUBServer(ServiceEntity se) {
        Set<String> pubClass = se.getPubClass();
        if (pubClass.isEmpty()) {
            return;
        }
        StringBuilder querySQL = new StringBuilder("select applicationName,applicationBaseUrl,subClass from service_entity where ");
        int i = 1;
        for (String ignored : pubClass) {
            if (i == pubClass.size()) {
                querySQL.append("subClass like ?");
            } else {
                querySQL.append("subClass like ? or");
            }
            i++;
        }
        try {
            Connection conn = getConn();
            PreparedStatement ps = conn.prepareStatement(querySQL.toString());
            int j = 1;
            for (String pub : pubClass) {
                ps.setString(j, "%" + pub + "%");
                j++;
            }
            ResultSet rs = ps.executeQuery();
            List<ServiceEntity> list = new ArrayList<>();
            while (rs.next()) {
                ServiceEntity re = new ServiceEntity();
                re.setApplicationName(rs.getString(1));
                re.setApplicationBaseUrl(rs.getString(2));
                re.setSubClass(Arrays.stream(rs.getString(3).split(",")).collect(Collectors.toSet()));
                list.add(re);
            }
            JdbcUtils.close(rs);
            JdbcUtils.close(ps);
            JdbcUtils.close(conn);
            submitQuery(se, list);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }

    }

    private void submitQuery(ServiceEntity pub, List<ServiceEntity> list) {
        Map<String, Map<String, MethodBean>> mjs = pub.getMethodsJson();
        Set<String> pubClass = pub.getPubClass();
        for (ServiceEntity se : list) {
            Set<String> subClass = se.getSubClass();
            Map<String, Map<String, MethodBean>> map = new HashMap<>();
            subClass.forEach(sub -> {
                if (pubClass.contains(sub)) {
                    Map<String, MethodBean> stringMethodBeanMap = mjs.get(sub);
                    map.put(sub, stringMethodBeanMap);
                }
            });
            smartHttpPOSTJSON(se.getApplicationBaseUrl() + insert, map);
        }
    }

    public int existBean(String applicationName, String applicationBaseUrl) {
        String countSQL = "select count(0) from service_entity where  applicationName= ? and applicationBaseUrl= ?";
        try {
            Connection conn = getConn();
            PreparedStatement ps = conn.prepareStatement(countSQL);
            ps.setString(1, applicationName);
            ps.setString(2, applicationBaseUrl);
            ResultSet rs = ps.executeQuery();
            if (rs.next()) {
                return rs.getInt(1);
            }
            JdbcUtils.close(rs);
            JdbcUtils.close(ps);
            JdbcUtils.close(conn);
            return 0;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    public void updateSurvivalTime(String applicationName, String applicationBaseUrl) {
        String updateSQL = "update service_entity set survivalTime = ? where  applicationName= ? and applicationBaseUrl=?";
        try {
            Connection conn = getConn();
            PreparedStatement ps = conn.prepareStatement(updateSQL);
            ps.setTimestamp(1, Timestamp.from(Instant.now()));
            ps.setString(2, applicationName);
            ps.setString(3, applicationBaseUrl);
            int i = ps.executeUpdate();
            JdbcUtils.close(ps);
            JdbcUtils.close(conn);
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }


    public Map<String, List<Map<String, MethodBean>>> getPUBServer(ServiceEntity es) {
        Set<String> subClass = es.getSubClass();
        if (subClass.isEmpty()) return null;
        StringBuffer selectSQL = new StringBuffer("select applicationName,applicationBaseUrl,methodsJson from service_entity where ");
        int i = 1;
        for (String ignored : subClass) {
            if (i == subClass.size()) {
                selectSQL.append("pubClass like ?");
            } else {
                selectSQL.append("pubClass like ? or");
            }
            i++;
        }
        Map<String, List<Map<String, MethodBean>>> result = new HashMap<>();
        try {
            Connection conn = getConn();
            PreparedStatement ps = conn.prepareStatement(selectSQL.toString());
            int j = 1;
            for (String sub : subClass) {
                ps.setString(j, "%" + sub + "%");
                j++;
            }
            ResultSet rs = ps.executeQuery();
            while (rs.next()) {
                for (String sub : subClass) {
                    String json = rs.getString(3);
                    Map<String, Map<String, MethodBean>> mapList = om.readValue(json, new TypeReference<>() {
                    });
                    Map<String, MethodBean> methodBeans = mapList.get(sub);
                    if (methodBeans != null) {
                        if (result.containsKey(sub)) {
                            result.get(sub).add(methodBeans);
                        } else {
                            List<Map<String, MethodBean>> list = new ArrayList<>();
                            list.add(methodBeans);
                            result.put(sub, list);
                        }
                    }
                }
            }
            JdbcUtils.close(rs);
            JdbcUtils.close(ps);
            JdbcUtils.close(conn);
        } catch (SQLException | IOException e) {
            throw new RuntimeException(e);
        }

        return result;
    }


    public List<Map<String, Object>> getAll() {
        try {
            Connection conn = getConn();
            PreparedStatement ps = conn.prepareStatement("select * from family.service_entity");
            ResultSet rs = ps.executeQuery();
            ResultSetMetaData md = rs.getMetaData();
            int cc = md.getColumnCount();
            List<Map<String, Object>> list = new ArrayList<>();
            while (rs.next()) {
                Map<String, Object> map = new HashMap<>();
                for (int i = 0; i < cc; i++) {
                    String columnName = md.getColumnName(i + 1);
                    map.put(columnName, rs.getObject(columnName));
                }
                list.add(map);
            }
            JdbcUtils.close(rs);
            JdbcUtils.close(ps);
            JdbcUtils.close(conn);
            return list;
        } catch (SQLException e) {
            throw new RuntimeException(e);
        }
    }
}